package com.imooc.distributedemo.common;

import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Slf4j
public class ZkLock implements AutoCloseable, Watcher {

    private ZooKeeper zooKeeper;
    private String znode;

    public ZkLock() throws IOException{
        this.zooKeeper = new ZooKeeper("localhost:2181",1000,this);

    }

    public boolean getLock(String businessCode){
        try {
            Stat stat = zooKeeper.exists("/" + businessCode, false);
            if(stat==null){
                // 创建业务根节点 /order
                zooKeeper.create("/"+businessCode,businessCode.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }

            // 创建瞬时有序节点 /order/order
            znode = zooKeeper.create("/"+businessCode+"/"+businessCode+"_",businessCode.getBytes(),
                    Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            // 获取业务下所有的子节点
            List<String> childrenNodes = zooKeeper.getChildren("/"+businessCode,false);
            // 子节点排序
            Collections.sort(childrenNodes);
            // 获取序号最小的(第一个节点)
            String firstNode = childrenNodes.get(0);
            // 如果创建的节点是第一个子节点，获得锁
            if(znode.endsWith(firstNode)){
                return true;
            }
            //  不是第一个子节点，则监听前一个节点
            String lastNode = firstNode;
            for (String node:childrenNodes){
                if(znode.endsWith(node)){
                    zooKeeper.exists("/"+businessCode+"/"+lastNode,true);
                }else{
                    lastNode = node;
                }
            }
            synchronized (this) {
                wait();
            }
            return true;
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        if(watchedEvent.getType()== EventType.NodeDeleted){
            synchronized (this){
                notify();
            }

        }
    }

    @Override
    public void close() throws Exception {
        zooKeeper.delete(znode,-1);
        zooKeeper.close();
        log.info("我已经释放了锁");
    }

}
